package com.yanqu.road.server.manager.kuangmatch;

import com.yanqu.road.dao.impl.servercenter.CrossServerInfoDaoImpl;
import com.yanqu.road.entity.enums.eSystemId;
import com.yanqu.road.entity.kuang.group.KuangMatchGroup;
import com.yanqu.road.entity.servercenter.ChannelSystemConfig;
import com.yanqu.road.entity.servercenter.CrossServerInfo;
import com.yanqu.road.entity.servercenter.ServerInfo;
import com.yanqu.road.logic.bussiness.kuang.KuangBusiness;
import com.yanqu.road.logic.config.ChannelConfig;
import com.yanqu.road.logic.config.GameConfig;
import com.yanqu.road.logic.pb.CrossKuangPb;
import com.yanqu.road.pb.kuang.Kuang;
import com.yanqu.road.server.TempCrossMgr;
import com.yanqu.road.server.connect.CrossAgentLinkedControl;
import com.yanqu.road.server.logic.MessageHelper;
import com.yanqu.road.server.manager.config.CrossSystemLoopMgr;
import com.yanqu.road.server.manger.ChannelSystemConfigMgr;
import com.yanqu.road.server.manger.ServerListMgr;
import com.yanqu.road.server.protocol.CrossProtocol;
import com.yanqu.road.server.protocol.Protocol;
import com.yanqu.road.utils.ConfigHelper;
import com.yanqu.road.utils.CrossServerType;
import com.yanqu.road.utils.YanQuMessageUtils;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.string.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 矿场争夺战匹配 -- 商会跨服
 * 这里只负责分组
 */
public class CrossKuangMatchMgr extends TempCrossMgr {

    private static AtomicInteger crossIndex = new AtomicInteger(new Random().nextInt(100));

    /**
     * 匹配分组信息
     */
    private static Map<Integer, Map<Integer, KuangMatchGroup>> matchGroupMap = new ConcurrentHashMap<>();

    private static List<CrossServerInfo> activityCrossList = new ArrayList<>();

    /**
     * 分组id
     */
    private static AtomicInteger groupId = new AtomicInteger(0);

    /**
     * 分组规则配置
     */
    private static ChannelSystemConfig rule;

    public static void checkGroupTask() {
        // 时间拦截一下
        List<Long> kuangTimeList = StringUtils.stringToLongList(GameConfig.KUANG_TIME, "\\|");
        long weekStartTime = DateHelper.getThisWeekStartTime(GameConfig.DAY_SYSTEM_RESET_TIME);
        // 时间需要减掉前几天
        weekStartTime = weekStartTime - (GameConfig.DAY_SYSTEM_RESET_TIME - 1) * DateHelper.DAY_MILLIONS;
        // 展示期开始时间/开启自动报名时间
        if (System.currentTimeMillis() < weekStartTime + kuangTimeList.get(0) * 60L * 1000L) {
            return;
        }
        int thisWeekId = GameConfig.getSystemThisWeekId();
        if (matchGroupMap.containsKey(thisWeekId) && !matchGroupMap.get(thisWeekId).isEmpty()) {
            return;
        }
        // 生成分组
        if (!matchGroupMap.containsKey(thisWeekId)) {
            matchGroupMap.put(thisWeekId, new ConcurrentHashMap<>());
        }
        // 渠道下所有的区服
        List<ServerInfo> serverList = CrossSystemLoopMgr.getLoopServerList(eSystemId.Kuang.getValue());
        if (serverList.isEmpty()) {
            return;
        }
//        List<ServerInfo> serverList = ServerListMgr.getChannelServerList(ConfigHelper.getInt("channelId"));
        // 内网测试
        handleTestGameServerList(serverList);

        // 先分大分组(按开服天数,key为开服天数索引)
        Map<Integer, List<Long>> bigGroupMap = new HashMap<>();
        for (ServerInfo serverInfo : serverList) {
            int openDayRange = getOpenDayRange(serverInfo);
            if (openDayRange == -1) {
                continue;
            }
            List<Long> list = bigGroupMap.getOrDefault(openDayRange, new ArrayList<>());
            list.add(serverInfo.getServerId());
            bigGroupMap.put(openDayRange, list);
        }
        // 检查下大分组，不满足的区间需要合并
        checkBigGroupMap(bigGroupMap);
        // 开始分配
        Map<Integer, Integer> serverCntMap = getServerCntMap();
        for (Map.Entry<Integer, List<Long>> entry : bigGroupMap.entrySet()) {
            // 最小开服天数
            Integer openDay = entry.getKey();
            Integer serverCnt = serverCntMap.getOrDefault(openDay, 0);
            // 分成多少个分组
            List<Long> serverIdList = entry.getValue();
            int count = Math.max(1, BigDecimal.valueOf(serverIdList.size()).divide(BigDecimal.valueOf(serverCnt), 0, RoundingMode.HALF_UP).intValue());
            // 生成分组
            List<KuangMatchGroup> groupList = new ArrayList<>();
            for (int i = 0; i < count; i++) {
                KuangMatchGroup matchGroup = new KuangMatchGroup();
                matchGroup.setWeekId(thisWeekId);
                matchGroup.setGroupId(groupId.incrementAndGet());
                matchGroup.setServerIdList(new ArrayList<>());
                matchGroup.setOpenDay(openDay);
                groupList.add(matchGroup);
            }
            // 区服打乱一下
            Collections.shuffle(serverIdList);
            // 塞区服
            for (int i = 0; i < serverIdList.size(); i++) {
                groupList.get(i % count).getServerIdList().add(serverIdList.get(i));
            }
            // 指定跨服
            for (KuangMatchGroup matchGroup : groupList) {
                matchGroup.setCrossId(activityCrossList.get(crossIndex.incrementAndGet() % activityCrossList.size()).getCrossServerId());
                matchGroup.setAddTime(new Date());
                matchGroup.setInsertOption();
                matchGroupMap.get(thisWeekId).put(matchGroup.getGroupId(), matchGroup);
            }
        }
        // 同步下
        syncMatchGroup();
    }

    private static Map<Integer, Integer> getServerCntMap() {
        Map<Integer, Integer> map = new HashMap<>();
        if (rule == null) {
            return map;
        }
        String kuangServerGroupRule = rule.getValue();
        List<String> paramList = StringUtils.stringToStringList(kuangServerGroupRule, "\\|");
        for (String s : paramList) {
            List<Integer> list = StringUtils.stringToIntegerList(s, "-");
            map.put(list.get(0), list.get(1));
        }
        return map;
    }

    private static Map<Integer, Integer> getMinMatchCntMap() {
        Map<Integer, Integer> map = new HashMap<>();
        if (rule == null) {
            return map;
        }
        String kuangServerGroupRule = rule.getValue();
        List<String> paramList = StringUtils.stringToStringList(kuangServerGroupRule, "\\|");
        for (String s : paramList) {
            List<Integer> list = StringUtils.stringToIntegerList(s, "-");
            map.put(list.get(0), list.get(2));
        }
        return map;
    }

    private static int getMaxRangeCnt() {
        if (rule == null) {
            return 0;
        }
        String kuangServerGroupRule = rule.getValue();
        List<String> paramList = StringUtils.stringToStringList(kuangServerGroupRule, "\\|");
        return paramList.size();
    }

    private static List<Integer> getDayOpenList() {
        List<Integer> dayOpenList = new ArrayList<>();
        if (rule == null) {
            return dayOpenList;
        }
        String kuangServerGroupRule = rule.getValue();
        List<String> paramList = StringUtils.stringToStringList(kuangServerGroupRule, "\\|");
        for (String s : paramList) {
            List<Integer> list = StringUtils.stringToIntegerList(s, "-");
            dayOpenList.add(list.get(0));
        }
        return dayOpenList;
    }

    /**
     * 检查下大分组，不满足的区间需要合并
     */
    private static void checkBigGroupMap(Map<Integer, List<Long>> bigGroupMap) {
        // 开服区间,最小匹配数
        Map<Integer, Integer> minMatchCntMap = getMinMatchCntMap();
        // 找出不满足的区间
        List<Integer> needMergeRange = new ArrayList<>();
        for (Map.Entry<Integer, List<Long>> entry : bigGroupMap.entrySet()) {
            if (entry.getValue().size() < minMatchCntMap.getOrDefault(entry.getKey(), 0)) {
                needMergeRange.add(entry.getKey());
            }
        }
        // 处理不满足的区间
        for (Integer range : needMergeRange) {
            List<Long> list = bigGroupMap.get(range);
            // 这边再校验一次，防止有中途插进来的
            if (list == null || list.size() >= minMatchCntMap.getOrDefault(range, 0)) {
                continue;
            }
            // 找临近的区间最小的
            int minRange = findMinRange(range, bigGroupMap);
            if (minRange == -1) {
                // 没得找
                continue;
            }
            // 加到临近区间
            List<Long> list1 = bigGroupMap.getOrDefault(minRange, new ArrayList<>());
            list1.addAll(list);
            bigGroupMap.put(minRange, list1);
            // 移除当前的区间
            bigGroupMap.remove(range);
        }
    }

    /**
     * 双指针查找直到找到满足的
     */
    public static int findMinRange(int range, Map<Integer, List<Long>> bigGroupMap) {
        int rangeIndex = findRangeIndex(range);
        if (rangeIndex == -1) {
            return -1;
        }
        // 往左找
        int tempLeftIndex = rangeIndex;
        int leftCnt = 0;
        int targetLeft = -1;
        while (--tempLeftIndex >= 0 && bigGroupMap.containsKey(findRangeByIndex(tempLeftIndex))) {
            leftCnt = bigGroupMap.getOrDefault(findRangeByIndex(tempLeftIndex), new ArrayList<>()).size();
            targetLeft = tempLeftIndex;
            // 找到跳出
            break;
        }
        // 往右找
        int tempRightIndex = rangeIndex;
        int rightCnt = 0;
        int targetRight = -1;
        while (++tempRightIndex < getMaxRangeCnt() && bigGroupMap.containsKey(findRangeByIndex(tempRightIndex))) {
            rightCnt = bigGroupMap.getOrDefault(findRangeByIndex(tempRightIndex), new ArrayList<>()).size();
            targetRight = tempRightIndex;
            // 找到跳出
            break;
        }
        if (targetLeft != -1 && targetRight != -1) {
            if (leftCnt <= rightCnt) {
                return findRangeByIndex(targetLeft);
            } else {
                return findRangeIndex(targetRight);
            }
        } else {
            if (targetLeft == -1 && targetRight == -1) {
                return -1;
            } else if (targetRight == -1) {
                return findRangeIndex(targetLeft);
            } else {
                return findRangeByIndex(targetRight);
            }
        }
    }

    public static int findRangeIndex(int range) {
        List<Integer> dayOpenList = getDayOpenList();
        for (int i = 0; i < dayOpenList.size(); i++) {
            if (range == dayOpenList.get(i)) {
                return i;
            }
        }
        return -1;
    }

    public static int findRangeByIndex(int index) {
        List<Integer> dayOpenList = getDayOpenList();
        for (int i = 0; i < dayOpenList.size(); i++) {
            if (index == i) {
                return dayOpenList.get(i);
            }
        }
        return -1;
    }


    /**
     * 取开服天数区间
     *
     * @param serverInfo
     */
    private static int getOpenDayRange(ServerInfo serverInfo) {
        if (serverInfo == null) {
            return -1;
        }
        long openDay = ServerListMgr.getServerOpenDay(serverInfo);
        List<Integer> dayOpenList = getDayOpenList();
        // 开服天数区间
        int range = -1;
        for (int day : dayOpenList) {
            if (openDay >= day) {
                range = Math.max(range, day);
            }
        }
        return range;
    }

    @Override
    public boolean removeExpireData() throws Exception {
        int thisWeekId = GameConfig.getSystemThisWeekId();
        int preWeekId = GameConfig.getSystemPreWeekId();
        for (Integer weekId : matchGroupMap.keySet()) {
            if (weekId != thisWeekId && weekId != preWeekId) {
                matchGroupMap.remove(weekId);
            }
        }
        return true;
    }

    @Override
    public boolean init() throws Exception {
        addManagerReloadListener();
        return reload();
    }

    public void addManagerReloadListener() {
        ChannelSystemConfigMgr.addReloadListener(() -> {
            reloadKuangConfig();
            getLogger().info("ChannelSystemConfigMgr addManagerReloadListener");
        });
    }

    public static void reloadKuangConfig() {
        // 加载配置
        int channelId = ConfigHelper.getInt("channelId");
        Map<String, ChannelSystemConfig> systemConfigMap = ChannelSystemConfigMgr.getSystemConfigMap(channelId, eSystemId.Kuang.getValue());
        if (systemConfigMap != null && systemConfigMap.containsKey("KUANGSHAN_SEVERGROUP_RULE")) {
            rule = systemConfigMap.get("KUANGSHAN_SEVERGROUP_RULE");
        }
    }

    @Override
    public boolean reloadConfig() throws Exception {
        reloadKuangConfig();
        return true;
    }

    @Override
    public boolean reloadData() throws Exception {
        initCrossList();
        // 获取本周的分组信息
        int weekId = GameConfig.getSystemThisWeekId();
        Map<Integer, KuangMatchGroup> groupMap = KuangBusiness.getKuangMatchGroupMap(weekId);
        matchGroupMap.put(weekId, groupMap);
        // 获取当前最大的分组id
        int maxGroupId = 0;
        for (KuangMatchGroup matchGroup : groupMap.values()) {
            maxGroupId = Math.max(maxGroupId, matchGroup.getGroupId());
        }
        groupId = new AtomicInteger(maxGroupId);
        return true;
    }


    private void initCrossList() {
        // 需要排除的跨服,往这里热更,匹配前改才可生效
        List<Long> needRemoveCrossIdList = new ArrayList<>();
        needRemoveCrossIdList.add(ChannelConfig.CHANNEL_ACTIVITY_CROSS_SERVER_ID);

        // 获取活动跨服
        int channelId = ConfigHelper.getInt("channelId");
        List<CrossServerInfo> list = new CrossServerInfoDaoImpl().getAllChannelCrossServerInfo(channelId).stream()
                .filter(item -> item.getCrossServerType() == CrossServerType.ACTIVITY_CROSS)
                .collect(Collectors.toList());
        if (list.size() > 1) {//活动跨服数大于1的才过滤跨域用活动跨服。
            list = list.stream().filter(item -> !needRemoveCrossIdList.contains((long) item.getCrossServerId())).collect(Collectors.toList());
        }

        // 内网测试处理
        handleTestCrossList(list);
        activityCrossList = list;
    }

    // 内网测试配置：活动跨服ID只能从这里面随机【外网不配置就行】
    public static void handleTestCrossList(List<CrossServerInfo> list) {
        String testCrossBattleServerId = ConfigHelper.getValue("kuangCrossBattleServerId");
        if (!StringUtils.isNullOrEmpty(testCrossBattleServerId) && !Objects.equals(testCrossBattleServerId, "0")) {
            List<Integer> selfCrossIdList = StringUtils.stringToIntegerList(testCrossBattleServerId, ";");
            List<CrossServerInfo> list2 = new ArrayList<>();
            for (CrossServerInfo crossServerInfo : list) {
                if (selfCrossIdList.contains(crossServerInfo.getCrossServerId())) {
                    list2.add(crossServerInfo);
                }
            }
            if (!list2.isEmpty()) {
                System.err.println("-------------------- [矿场分组] 已开启 内网活动跨服ID限制 --------------------");
                getLogger().error("-------------------- [矿场分组] 已开启 内网活动跨服ID限制 --------------------");
                list.clear();
                list.addAll(list2);
            }
        }
    }

    public static void handleTestGameServerList(List<ServerInfo> list) {
        String testGameServerId = ConfigHelper.getValue("kuangGameServerId");
        if (!StringUtils.isNullOrEmpty(testGameServerId) && !Objects.equals(testGameServerId, "0")) {
            List<Long> selfServerIdList = StringUtils.stringToLongList(testGameServerId, ";");
            List<ServerInfo> list2 = new ArrayList<>();
            for (ServerInfo serverInfo : list) {
                if (selfServerIdList.contains(serverInfo.getServerId())) {
                    list2.add(serverInfo);
                }
            }
            if (!list2.isEmpty()) {
                System.err.println("-------------------- [矿场分组] 已开启 内网区服ID限制 --------------------");
                getLogger().error("-------------------- [矿场分组] 已开启 内网区服ID限制 --------------------");
                list.clear();
                list.addAll(list2);
            }
        }
    }

    @Override
    public boolean stop() {
        return true;
    }

    @Override
    public boolean save() {
        for (Map<Integer, KuangMatchGroup> groupMap : matchGroupMap.values()) {
            for (KuangMatchGroup matchGroup : groupMap.values()) {
                if (matchGroup.isInsertOption()) {
                    KuangBusiness.addKuangMatchGroup(matchGroup);
                } else if (matchGroup.isUpdateOption()) {
                    KuangBusiness.updateKuangMatchGroup(matchGroup);
                }
            }
        }
        return true;
    }

    /**
     * 同步分组
     */
    public static void syncMatchGroup() {
        if (matchGroupMap.size() == 0) {
            return;
        }
        int thisWeekId = GameConfig.getSystemThisWeekId();
        Map<Integer, KuangMatchGroup> groupMap = matchGroupMap.get(thisWeekId);
        if (groupMap == null || groupMap.size() == 0) {
            return;
        }
        Map<Integer, List<KuangMatchGroup>> syncMap = new ConcurrentHashMap<>();
        for (KuangMatchGroup matchGroup : groupMap.values()) {
            List<KuangMatchGroup> list = syncMap.getOrDefault(matchGroup.getCrossId(), new ArrayList<>());
            list.add(matchGroup);
            syncMap.put(matchGroup.getCrossId(), list);
        }
        for (Map.Entry<Integer, List<KuangMatchGroup>> entry : syncMap.entrySet()) {
            int crossId = entry.getKey();
            // 分组信息
            List<KuangMatchGroup> groupList = entry.getValue();
            // 通知活动服更新
            notifyMatchGroup(crossId, groupList);
        }
    }

    public static void syncMatchGroup(long serverId) {
        if (matchGroupMap.size() == 0) {
            return;
        }
        int thisWeekId = GameConfig.getSystemThisWeekId();
        Map<Integer, KuangMatchGroup> groupMap = matchGroupMap.get(thisWeekId);
        if (groupMap.size() == 0) {
            return;
        }
        KuangMatchGroup group = null;
        for (KuangMatchGroup matchGroup : groupMap.values()) {
            if (matchGroup.getServerIdList().contains(serverId)) {
                group = matchGroup;
                break;
            }
        }
        if (group != null) {
            // 通知活动服更新
            notifyMatchGroup(group.getCrossId(), Collections.singletonList(group));
        }
    }

    /**
     * 通知活动跨服
     *
     * @param crossId        跨服id
     * @param matchGroupList 分组信息
     */
    public static void notifyMatchGroup(int crossId, List<KuangMatchGroup> matchGroupList) {
        List<Long> serverIdList = new ArrayList<>();
        // 同步到活动服
        Kuang.CrossNotifyMatchGroupToActivityMsg.Builder builder = Kuang.CrossNotifyMatchGroupToActivityMsg.newBuilder();
        builder.setCrossId(crossId);
        for (KuangMatchGroup matchGroup : matchGroupList) {
            Kuang.KuangMatchGroupTemp.Builder groupTemp = CrossKuangPb.parseKuangMatchGroupTemp(matchGroup);
            if (groupTemp != null) {
                builder.addGroup(groupTemp);
            }
            serverIdList.addAll(matchGroup.getServerIdList());
        }
        // 同步活动跨服
        MessageHelper.sendUnionActivityGroupPacket(
                CrossAgentLinkedControl.randomConnectionSeverId(),
                0,
                CrossProtocol.C_CROSS_KUANG_MATCH_GROUP_SYNC,
                builder,
                0,
                crossId,
                0);
        // 同步游戏服
        for (KuangMatchGroup matchGroup : matchGroupList) {
            Kuang.CrossNotifyMatchGroupToGameMsg.Builder gameBuilder = Kuang.CrossNotifyMatchGroupToGameMsg.newBuilder();
            Kuang.KuangMatchGroupTemp.Builder groupTemp = CrossKuangPb.parseKuangMatchGroupTemp(matchGroup);
            if (groupTemp == null) {
                continue;
            }
            gameBuilder.setGroup(groupTemp);
            for (Long serverId : matchGroup.getServerIdList()) {
                MessageHelper.sendPacket(serverId, 0, YanQuMessageUtils.buildMessage(Protocol.S_KUANG_NOTIFY_MATCH_GROUP, gameBuilder));
//                getLogger().info("notifyMatchGroup timer sync to serverID {}.", serverId);
            }
        }
    }


}
